#include "css_decoder.h"
#include "utils.h"
#include <iostream>
#include <algorithm>
using namespace std;
using namespace dymy;

CSSDecoder::CSSDecoder(float scale) : scale_(scale)
{
}

std::optional<float> CSSDecoder::px_to_dp(const std::string &px_text) const noexcept
{
    auto px = parse_int(px_text);
    if (px)
    {
        return static_cast<float>(px.value() * scale_);
    }
    return std::nullopt;
}

Widget *CSSDecoder::Decode(const std::string &code) const
{
    vector<string> values = split(code, kCSSSplit);
    if (values.size() == 0)
    {
        return nullptr;
    }
    unordered_map<string, string> styles;
    for (const string &v : values)
    {
        auto s = remove_end_semicolon(trim(v));
        if (s.empty() || s.front() == '/' || s.front() == ' ')
        {
            continue;
        }
        vector<string> line = split(s, kCSSAttrSplit);
        if (line.size() == 2)
        {
            const auto &k = line[0];
            auto &v = line[1];
            string_replace(v, ", ", ",");
            styles[k] = v;
        }
        else
        {
            cout << "decode failed,line:" << s << endl;
            return nullptr;
        }
    }
    if (styles.empty())
    {
        return nullptr;
    }

    if (contains_attr(styles, "font-size"))
    {
        return Styles2Text(styles);
    }
    else if (contains_attr(styles, "background") || contains_attr(styles, "border") || contains_attr(styles, "border-radius"))
    {
        return Styles2Container(styles);
    }
    return nullptr;
}

std::optional<EdgeInsets> CSSDecoder::parse_edge_insets(const std::string &s) const noexcept
{
    vector<string> vs = split(s, " ");
    if (vs.size() == 1)
    {
        auto a = px_to_dp(vs[0]);
        if (a)
        {
            return EdgeInsets(a.value(), a.value());
        }
    }
    else if (vs.size() == 2)
    {
        auto v = px_to_dp(vs[0]);
        auto h = px_to_dp(vs[1]);
        if (v && h)
        {
            return EdgeInsets(h.value(), v.value());
        }
    }
    else if (vs.size() == 3)
    {
        auto t = px_to_dp(vs[0]);
        auto l = px_to_dp(vs[1]);
        auto b = px_to_dp(vs[2]);
        if (t && l && b)
        {
            return EdgeInsets(l.value(), t.value(), 0, b.value());
        }
    }
    else if (vs.size() == 4)
    {
        auto t = px_to_dp(vs[0]);
        auto r = px_to_dp(vs[1]);
        auto b = px_to_dp(vs[3]);
        auto l = px_to_dp(vs[2]);
        if (t && r && b && l)
        {
            return EdgeInsets(l.value(), t.value(), r.value(), b.value());
        }
    }
    return std::nullopt;
}
std::optional<Container::Border> CSSDecoder::parseBorder(const std::string &s) const
{
    auto strs = split(s, " ");
    if (strs.size() == 3)
    {
        optional<float> width = px_to_dp(strs[0]);
        optional<int> color = parse_color(strs[2]);
        if (width.has_value() && color.has_value())
        {
            Container::Border b;
            b.border_color = color.value();
            b.border_width = width.value();
            return b;
        }
    }
    return nullopt;
}

Text *CSSDecoder::Styles2Text(const Styles &styles) const
{
    Text *text = new Text();
    text->type = WidgetType::Text;

    for (const auto &entries : styles)
    {
        const auto &key = entries.first;
        const auto &value = entries.second;
        if (key == "font-family")
        {
            text->font_family = value;
        }
        else if (key == "font-weight")
        {
            text->font_weight = parse_int(value);
        }
        else if (key == "font-size")
        {
            text->font_size = px_to_dp(value);
        }
        else if (key == "letter-spacing")
        {
            text->letter_space = px_to_dp(value);
        }
        else if (key == "line-height")
        {
            Styles::const_iterator p = styles.find("font-size");
            if (p != styles.cend())
            {
                auto font_size_px = parse_int(p->second);
                auto line_height = parse_int(value);
                if (font_size_px && line_height)
                {
                    text->line_height = static_cast<float>(line_height.value()) / font_size_px.value();
                }
            }
        }
        else if (key == "color")
        {
            text->color = parse_color(value);
        }
    }
    return text;
}

Container *CSSDecoder::Styles2Container(const Styles &styles) const
{
    Container *container = new Container();
    container->type = WidgetType::Container;
    for (const auto &entries : styles)
    {
        const auto &key = entries.first;
        const auto &value = entries.second;
        if (key == "border")
        {
            container->border = parseBorder(value);
            // text->font_family = value;
        }
        else if (key == "border-radius")
        {
            container->border_radius = px_to_dp(value);
        }
        else if (key == "padding")
        {
            container->padding = parse_edge_insets(value);
            // container->padding = px_to_dp(value);
        }

        else if (key == "background")
        {
            container->background = parse_color(value);
        }
    }
    return container;
}
